import random
from dataclasses import dataclass
from enum import Enum
from typing import Any

from skills import meta, skills_hook_map


class CombatFieldCardType(Enum):
    Environment = 0
    Avatar = 1
    Skill = 2


@dataclass
class CombatFieldCard:
    name: str
    index: int
    widget: Any
    time: int
    type: CombatFieldCardType


##########################################################################################################################################
###########################        游戏开始前的内容         #################################################################################
##########################################################################################################################################
class main_window(meta):
    def step0(self, event, status, game, ui):
        game.pause()
        event.forward()  # show_main_window(sys.exit(app.exec())) never return so we increase event.step manually
        ui.show_main_window(game.resume)

    def step1(self, event, status, game, ui):
        event = event.create_event("choose_room", triggering=0)
        event.set_content(choose_room)

    def step2(self, event, status, game, ui):
        event = event.create_event("choose_character", triggering=0)
        event.set_content(choose_character)

    def step3(self, event, status, game, ui):
        new_event = event.create_event("game_start", triggering=0)
        new_event.set_content(game_start)
        new_event.prompt = event.child_result


class choose_room(meta):
    def step0(self, event, status, game, ui):
        ui.delete_widget_item("main_window_buttons")
        ui.create_rooms()
        ui.set_rooms_callback(event)

    def step1(self, event, status, game, ui):
        game.msg_send = "get_room"
        game.pause()

    def step2(self, event, status, game, ui):
        game.pause()

    def step3(self, event, status, game, ui):
        rooms = eval(game.msg_recv)
        game.msg_recv = None
        ui.fresh_rooms(rooms)

    def step4(self, event, status, game, ui):
        if event.num is None:
            event.goto(1)
            return

        ui.set_my_room(event.num)
        game.msg_send = f"get_in_room{event.num}"
        game.pause()

    def step5(self, event, status, game, ui):
        game.msg_send = f"room_ready"
        game.pause()

    def step6(self, event, status, game, ui):
        game.pause()

    def step7(self, event, status, game, ui):
        if game.msg_recv != "game_begin":
            raise RuntimeError("msg error")
        game.msg_recv = None

class choose_character(meta):
    def step0(self, event, status, game, ui):
        from player import player_map
        players = [info.name for info in player_map.values()]
        ui.delete_widget_item("rooms")
        ui.create_characters(players)
        ui.set_character_callback(event)

    def step1(self, event, status, game, ui):
        game.callback = lambda: event.prompt is not None
        game.pause()

    def step2(self, event, status, game, ui):
        game.msg_send = f"choose_character:{event.prompt}"
        ui.delete_widget_item("characters")
        ui.prompt(f"已选择英雄{event.prompt}")
        game.pause()

    def step3(self, event, status, game, ui):
        game.pause()

    def step4(self, event, status, game, ui):
        event.result = eval(game.msg_recv)
        game.msg_recv = None


############################################################################################################################################
##########################          游戏流程       ###########################################################################################
############################################################################################################################################
###############  游戏开始  ##################################################
class game_start(meta):
    def step0(self, event, status, game, ui):
        ui.prompt()
        ui.init_combat_field()
        ui.init_logger()

    def step1(self, event, status, game, ui):
        from player import create_player
        event.player = create_player(event.prompt[0], event.prompt[2] == 0, status)

    def step2(self, event, status, game, ui):
        from player import create_player
        event.enemy = create_player(event.prompt[1], event.prompt[2] == 1, status)

    def step3(self, event, status, game, ui):
        event.player.enemy, event.enemy.enemy = event.enemy, event.player
        ui.add_widget_item("player_me", event.player)
        ui.add_widget_item("player", event.enemy)

    def step4(self, event, status, game, ui):
        game.msg_send = "get_seed"
        game.pause()

    def step5(self, event, status, game, ui):
        game.pause()

    def step6(self, event, status, game, ui):
        seed = int(game.msg_recv)
        random.seed(seed)
        game.msg_recv = None

    def step7(self, event, status, game, ui):
        event = event.create_event("create_card_pile", triggering=0)
        event.set_content(create_card_pile)

    def step8(self, event, status, game, ui):
        new_event = event.create_event("game_begin", triggering=0)
        new_event.player, new_event.enemy = event.player, event.enemy
        new_event.set_content(empty_event)

    def step9(self, event, status, game, ui):
        status.current_phase = event.player if random.randint(1, 10000000) % 2 else event.enemy

    def step10(self, event, status, game, ui):
        game.log("游戏开始")
        event = event.create_event("phase_loop", triggering=0)
        event.set_content(phase_loop)


#####################牌堆相关#####################################################
class create_card_pile(meta):
    def step0(self, event, status, game, ui):
        from card import create_card_pile
        status.card_pile, status.cards_index = create_card_pile()
        status.wash_card_pile()


#####################phase相关###################################################
class phase_loop(meta):
    def step0(self, event, _status, game, ui):
        event = event.create_event("phase_begin", triggering=0)
        event.set_content(empty_event)
        event.player = _status.current_phase
        game.log(f"{event.player.name}回合开始")

    def step1(self, event, _status, game, ui):
        event = event.create_event("phase_draw", triggering=0)
        event.set_content(phase_draw)
        event.player = _status.current_phase
        event.num = 2
        game.log(f"{event.player.name}摸牌开始")

    def step2(self, event, _status, game, ui):
        event = event.create_event("phase_use", triggering=0)
        event.set_content(phase_use)
        event.player = _status.current_phase
        game.log(f"{event.player.name}出牌开始")

    def step3(self, event, _status, game, ui):
        event = event.create_event("phase_discard", triggering=0)
        event.set_content(phase_discard)
        event.player = _status.current_phase
        game.log(f"{event.player.name}弃牌开始")

    def step4(self, event, _status, game, ui):
        event = event.create_event("phase_end", triggering=0)
        event.set_content(empty_event)
        event.player = _status.current_phase
        game.log(f"{event.player.name}回合结束")

    def step5(self, event, _status, game, ui):
        _status.current_phase = _status.current_phase.enemy
        event.goto(0)


class phase_draw(meta):
    def step0(self, event, _status, game, ui):
        event.player.draw_cards(event.num)


class phase_use(meta):
    def step0(self, event, _status, game, ui):
        if event.player.is_me():
            event.goto(1)
        else:
            event.goto(4)

    def step1(self, event, _status, game, ui):
        game.pause()
        event.player.action = None
        game.callback = lambda: event.player.action is not None

    def step2(self, event, _status, game, ui):
        type, entity = event.player.action
        if type == "click_card":
            event.player.use_card(entity)
            game.msg_send = f"{type}:{entity.index}"
            game.pause()
        elif type == "phase_end":
            game.msg_send = f"{type}:{entity}"
            game.pause()
            event.finish()
        elif type == "use_coin":
            game.msg_send = f"{type}:{entity}"
            game.pause()
            event.player.use_coin()

    def step3(self, event, _status, game, ui):
        event.goto(1)

    def step4(self, event, _status, game, ui):
        game.pause()

    def step5(self, event, _status, game, ui):
        msg = game.msg_recv
        game.msg_recv = None

        type, entity = msg.split(":")
        if type == "click_card":
            index = int(entity)
            event.player.use_card(index)
        elif type == "phase_end":
            event.finish()
        elif type == "use_coin":
            event.player.use_coin()

    def step6(self, event, _status, game, ui):
        event.goto(4)


class phase_discard(meta):
    def step0(self, event, _status, game, ui):
        num = len(event.player.hand_cards) - 7
        if num > 0:
            event.player.discard(num)


class empty_event(meta):
    def step0(self, event, _status, game, ui):
        game.trigger(event.name)


###################################arrange_trigger##############################################################
class arrange_trigger(meta):
    def step0(self, event, _status, game, ui):
        event.triggered_skills = skills_hook_map.get(event.prompt)
        event.player = event.parent.player
        event.enemy = event.player.enemy

    def step1(self, event, _status, game, ui):
        if event.triggered_skills:
            skill = event.triggered_skills.pop()
            event.skill = skill

            if skill.trigger_type() == "Player":
                event.users = [event.player]
            elif skill.trigger_type() == "Target":
                event.users = [event.enemy]
            elif skill.trigger_type() == "Global":
                event.users = [event.player, event.enemy]

            if skill.skill_type() in ["player_skill", "global_skill"]:
                event.goto(2)
            elif skill.skill_type() == "card_skill":
                event.goto(4)
        else:
            event.finish()

    def step2(self, event, _status, game, ui):
        if event.users:
            user = event.users.pop()
            skill = event.skill
            if skill.skill_type() == "player_skill" and not user.can_use_skill(skill):
                event.redo()
                return
            try:
                if not skill.filter(event.parent, _status, game, ui):
                    event.redo()
                    return
            except Exception as e:
                pass

            if skill.forced:
                user.use_skill(skill)
                event.redo()
            else:
                user.choose_bool(prompt=f"是否使用技能{skill.get_name()}")
                event.user = user
        else:
            event.goto(1)

    def step3(self, event, _status, game, ui):
        if event.child_result:
            event.user.use_skill(event.skill)

        event.goto(2)

    def step4(self, event, _status, game, ui):
        if event.users:
            user = event.users.pop()
            skill = event.skill
            card = user.find_card_by_skill(skill)
            if card is None:
                event.redo()
                return
            try:
                if not skill.filter(event.parent, _status, game, ui):
                    event.redo()
                    return
            except Exception as e:
                pass

            if skill.forced:
                user.use_card(card)
                event.redo()
            else:
                user.choose_bool(prompt=f"是否使用卡牌{card.name}")
                event.user = user
                event.card = card
        else:
            event.goto(1)

    def step5(self, event, _status, game, ui):
        if event.child_result:
            event.user.use_card(event.card)

        event.goto(4)


############################################################################################################################################
##########################          玩家相关       ###########################################################################################
############################################################################################################################################
##############   战场卡牌操作    ###############################
class add_environment_card(meta):
    def step0(self, event, status, game, ui):
        widget = ui.add_environment_card(event.index, f"resource/image/environment/{event.prompt}.png")
        status.combat_field_cards[event.index].append(
                CombatFieldCard(event.prompt, event.index, widget, event.num, CombatFieldCardType.Environment))


class remove_environment_card(meta):
    def step0(self, event, status, game, ui):
        status.combat_field_cards[event.index].remove(event.card)
        ui.remove_environment_card(event.card.index, event.card.widget)


class move_environment_card(meta):
    def step0(self, event, status, game, ui):
        status.combat_field_cards[event.index_from].remove(event.card)
        status.combat_field_cards[event.index_to].append(event.card)
        ui.move_environment_card(event.index_from, event.index_to, event.card.widget)


class add_avatar_card(meta):
    def step0(self, event, status, game, ui):
        widget = ui.add_avatar_card(event.index, f"resource/image/character/{event.prompt}.png")
        avatar_card = CombatFieldCard(event.prompt, event.index, widget, event.num, CombatFieldCardType.Avatar)
        status.combat_field_cards[event.index].append(avatar_card)
        event.player.avatar_card = avatar_card


class remove_avatar_card(meta):
    def step0(self, event, status, game, ui):
        status.combat_field_cards[event.index].remove(event.card)
        ui.remove_avatar_card(event.card.index, event.card.widget)


class move_avatar_card(meta):
    def step0(self, event, status, game, ui):
        status.combat_field_cards[event.index_from].remove(event.card)
        status.combat_field_cards[event.index_to].append(event.card)
        ui.move_avatar_card(event.index_from, event.index_to, event.card.widget)


class add_skill_card(meta):
    def step0(self, event, status, game, ui):
        widget = ui.add_skill_card(event.index, f"resource/image/skill/{event.prompt}.png")
        status.combat_field_cards[event.index].append(
                CombatFieldCard(event.prompt, event.index, widget, event.num, CombatFieldCardType.Skill))


class remove_skill_card(meta):
    def step0(self, event, status, game, ui):
        status.combat_field_cards[event.index].remove(event.card)
        ui.remove_skill_card(event.card.index, event.card.widget)


class move_skill_card(meta):
    def step0(self, event, status, game, ui):
        status.combat_field_cards[event.index_from].remove(event.card)
        status.combat_field_cards[event.index_to].append(event.card)
        ui.move_skill_card(event.index_from, event.index_to, event.card.widget)


###################获得金币#################################
class change_coin(meta):
    def step0(self, event, status, game, ui):
        event.player.coin += event.num
        event.player.flush_coin()


###################使用金币#################################
class use_coin(meta):
    def step0(self, event, status, game, ui):
        if event.player.coin >= 3:
            event.player.change_coin(-3)
            event.player.level_up()


###################升级####################################
class level_up(meta):
    def step0(self, event, status, game, ui):
        adds = event.player.type[event.player.level]
        for key in adds:
            if key == "attack":
                event.player.attack += adds[key]
            elif key == "skill_refactor":
                event.player.skill_refactor += adds[key]
            elif key == "max_hp":
                event.player.change_max_hp(adds[key])
            elif key == "skill":
                event.player.add_skill(adds[key])
        event.player.level += 1
        event.player.flush_level()


#######################################################
class change_max_hp(meta):
    def step0(self, event, status, game, ui):
        event.player.max_hp += event.num
        event.player.hp += event.num
        event.player.set_hp()


############################################################################################################################################
##########################          技能相关       ###########################################################################################
############################################################################################################################################
class use_skill(meta):
    def step0(self, event, status, game, ui):
        new_event = status.event.create_event(event.skill.get_name())
        new_event.player = event.player
        new_event.set_content(event.skill)


############################################################################################################################################
##########################          卡牌相关       ###########################################################################################
############################################################################################################################################
class draw_cards(meta):
    def step0(self, event, status, game, ui):
        if len(status.card_pile) < event.num:
            status.wash_card_pile()

        for _ in range(event.num):
            card = status.card_pile.pop()
            card.player = event.player
            card.set_background(card.id, event.player.is_me())
            event.player.add_hand_card(card)
        event.player.set_hand_card_num()


class lose_hand_cards(meta):
    def step0(self, event, status, game, ui):
        for card in event.cards:
            card.player = None
            event.player.remove_hand_card(card)
            status.discard_card_pile.append(card)
        event.player.set_hand_card_num()


class use_card(meta):
    def step0(self, event, status, game, ui):
        game.log(f"{event.player.name}使用了卡牌{event.card.name}")
        event.player.use_skill(event.card.skill)

    def step1(self, event, status, game, ui):
        game.log(f"{event.player.name}失去卡牌{event.card.name}")
        event.player.lose_hand_cards([event.card])


############################################################################################################################################
##########################          战场相关       ###########################################################################################
############################################################################################################################################
class choose_bool(meta):
    def step0(self, event, status, game, ui):
        ui.choose_bool(event.prompt, event)
        game.callback = lambda: event.result is not None
        game.pause()


class choose_field(meta):
    def step0(self, event, _status, game, ui):
        if event.player.is_me():
            event.goto(1)
        else:
            event.goto(3)
            return

    def step1(self, event, _status, game, ui):
        fields = event.fields
        if isinstance(fields, int):
            fields = [i for i in range(event.player.pos - fields, event.player.pos + fields + 1)]
            for i in list(fields):
                if i > 6 or i < 0:
                    fields.remove(i)
            if not event.to_self:
                fields.remove(event.player.pos)
        event.fields = fields
        ui.choose_field(fields, event)

        game.pause()
        game.callback = lambda: event.result is not None

    def step2(self, event, _status, game, ui):
        game.msg_send = str(event.result)
        game.pause()
        ui.remove_choose_field(event.fields)
        event.finish()

    def step3(self, event, _status, game, ui):
        game.pause()

    def step4(self, event, _status, game, ui):
        event.result = game.msg_recv
        event.result = 6 - int(event.result)
        game.msg_recv = None


class move_to(meta):
    def step0(self, event, status, game, ui):
        new_event = event.create_event("move_avatar_card", triggering=0)
        new_event.player = event.player
        new_event.index_from = event.player.pos
        new_event.index_to = event.pos
        new_event.card = event.player.avatar_card
        new_event.set_content(move_avatar_card)
        new_event.player.pos = event.pos
        game.log(f"{event.player.name}移动到了{event.pos}")
